package com.damai.action.json;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.citywithincity.api.ApiConfig;
import com.citywithincity.api.entities.Customer;
import com.citywithincity.reflect.CtClassUtil;
import com.citywithincity.utils.CachedClassUtil;
import com.citywithincity.utils.ClassUtil;
import com.damai.action.IParamParser;
import com.damai.action.IParamParserFactory;
import com.damai.action.json.ParamGetterFactory.IParamGetterFactory;
import com.damai.annotation.Crypt;
import com.damai.annotation.IgnoreFile;
import com.damai.annotation.Param;
import com.damai.annotation.ReturnString;
import com.damai.annotation.Token;
import com.damai.annotation.UploadFileList;

import javassist.ClassPool;
import javassist.bytecode.stackmap.BasicBlock.Maker;

public class JsonParamParserFactory implements IParamParserFactory, IParamGetterFactory {

	private static final JsonParamParserFactory DEFAULT = new JsonParamParserFactory();
	
	public static final JsonParamParserFactory getDefault(){
		return DEFAULT;
	}
	private JsonParamParserFactory(){
		
	}
	
	
	@Override
	public IParamParser createParser(ClassPool classPool,Class<?> controllerClass, Method method) {
		Object controller = CachedClassUtil.newController(controllerClass);
		return new JsonParam(createGetJson(controller,method),
				createParamGetter(classPool, controller,method));
	}
	
	public static boolean containsFile(Method method){
		if(method.isAnnotationPresent(IgnoreFile.class)){
			return false;
		}
		Class<?>[] types = method.getParameterTypes();
		return checkContainsFile(types,method);
	}
	
	private boolean needsDecrypt(Method method){
		Crypt crypt = method.getAnnotation(Crypt.class);
		if(crypt==null)return false;
		if(  (crypt.value() & Crypt.UPLOAD) >0 ){
			return true;
		}
		return false;
	}
	
	private IGetJson createGetJson(Object controller, Method method){
		IGetJson getJson = null;
		UploadFileList list = method.getAnnotation(UploadFileList.class);
		if(list==null){
			if(containsFile(method)){
				if(needsDecrypt(method)){
					getJson = new DecFileGetJson();
				}else{
					getJson = new FileGetJson();
				}
			}else{
				if(needsDecrypt(method)){
					getJson = new DecGetJson();
				}else{
					getJson = new DefGetJson();
				}
			}
		}else{
			if(needsDecrypt(method)){
				return new FileDecryptParamParser(list);
			}else{
				return new FileParamParser(list);
			}
		}
		
		//这里面需要检查是否需要登录
		Token token = controller.getClass().getAnnotation(Token.class);
		if(token == null){
			token = method.getAnnotation(Token.class);
		}
		if(token!=null){
			getJson = new TokenGetJson(getJson,token.value());
		}
		return getJson;
	}

	private IParamGetter[] createParamGetter(ClassPool classPool,Object controller, Method method){
		
		Class<?> returnType = method.getReturnType();
		if(returnType == String.class ){
			if(!method.isAnnotationPresent(ReturnString.class)){
				throw new RuntimeException("Controller "+ controller.getClass() + " method " + method + " return type is String!");
			}
			
		}
		
		Class<?> clazz = controller.getClass();
		
		try{
			String[] names = CtClassUtil.getParams( classPool, controller.getClass(),method);
			int index = 0;
			List<IParamGetter> list = new ArrayList<IParamGetter>(names.length);
			for (Class<?> type : method.getParameterTypes()) {
				if(type==List.class){
					Annotation[][] an = method.getParameterAnnotations();
					if(an[index] != null && an[index].length > 0){
						Param param = (Param)an[index][0];
						if(param!=null){
							Class<?> paramType = param.clazz();
							list.add(ParamGetterFactory.createGetter( clazz,method, names[index++], type,paramType,this));
							continue;
						}
					}
				}
				list.add(ParamGetterFactory.createGetter( clazz,method, names[index++], type,null,this));
			}
			IParamGetter[] getters = new IParamGetter[list.size()];
			list.toArray(getters);
			return getters;
		}catch(Exception e){
			
			ApiConfig.error("Create param getter error:" + controller.getClass().getName() + " method:" + method.getName(),e);
			throw new RuntimeException(e);
		}
		
	}
	
	
	private static boolean checkContainsFile(Field[] fields){
		for (Field field : fields) {
			Class<?> type = field.getType();
			if(type==File.class || type==byte[].class){
				return true;
			}else if(type==List.class){
				Class<?> fieldType = ClassUtil.getFieldListGenType(field);
				if(ClassUtil.isSimpleClass(fieldType)){
					continue;
				}
				if(fieldType==File.class || fieldType==byte[].class){
					return true;
				}
				if(fieldType==Map.class){
					continue;
				}
				if(fieldType==List.class){
					continue;
				}
				if(beanContainsFile(fieldType)){
					return true;
				}
			}else if(type==Map.class){
				continue;
			}else if(ClassUtil.isSimpleClass(type)){
				continue;
			}else {
				if(beanContainsFile(type)){
					return true;
				}
			}
		}
		return false;
	}
	
	
	private static boolean beanContainsFile(Class<?> type){
		Field[] fields = ClassUtil.getFields(type);
		if(checkContainsFile(fields)){
			return true;
		}
		return false;
	}
	

	
	private static boolean checkContainsFile(Class<?>[] types,Method method){
		int index = 0;
		for (Class<?> type : types) {
			if(type==File.class || type==byte[].class){
				return true;
			}else if(type==List.class){
				
				Annotation[][] an = method.getParameterAnnotations();
				if(an[index] != null && an[index].length > 0){
					Param param = (Param)an[index][0];
					if(param!=null){
						Class<?> paramType = param.clazz();
						if(checkContainsFile(ClassUtil.getFields(paramType))){
							return true;
						}
					}
				}
			}else if(type.isPrimitive()){
				continue;
			}else if(type.getName().startsWith("java")){
				continue;
			}else{
				if(beanContainsFile(type)){
					return true;
				}
			}
			++index;
		}
		
		return false;
	}
	@Override
	public IParamGetter createGetter(Class<?> controller, Method method, String name, Class<?> type,
			Class<?> paramType) {
		if (type == Customer.class) {
			return new SimpleParamGetter(name);
		}
		return null;
	}
}
